﻿using System;
using System.Collections.Generic;

namespace LOHDemo
{
	// This test will allocate small objects, which will therefore
	// be allocated into the small object heap. We interlace the instances
	// across two lists as before until we run out of space. Once that happens, we
	// release and collect one of the lists and notice that, unlike the same test on the 
	// large object heap, the subsequent allocation passes. This is because the small 
	// object heap is compacted during a collection.
	public class TestSmallObjectHeapInterlacedAllocationDoesCompaction : ITest
	{
		public void Execute(IHost host)
		{
			host.StartTest("Test four - filling SOH with interlaced small objects");
			host.MemoryUsed("Start of test");
			List<SmallObjectUnderThresholdForLOH> first = new List<SmallObjectUnderThresholdForLOH>();
			List<SmallObjectUnderThresholdForLOH> second = new List<SmallObjectUnderThresholdForLOH>();

			// Allocate interlaced memory until it runs out
			try
			{
				while (true)
				{
					first.Add(new SmallObjectUnderThresholdForLOH());
					second.Add(new SmallObjectUnderThresholdForLOH());
				}
			}
			catch (OutOfMemoryException)
			{
			}

			// SOH is now fill
			host.MemoryUsed("Memory is now full.");

			// Clear one of the lists which will result in the heap having
			// half memory free, but in 1mb holes. We then force a
			// full collection and this will compact the heap.
			first.Clear();
			first = null;
			GC.Collect(2, GCCollectionMode.Forced);
			host.MemoryUsed("After freeing first list.");

			// Now we have plenty of free space on the heap, let's try to allocate
			// a slightly larger object (which without compaction wouldn't
			// be able to allocate but are still within the SOH threshold)
			byte[] shouldfit = new byte[Constants.LargerObjectOnSmallObjectHeap];
			host.MemoryUsed("Successfully allocated larger SOH object - holes must have been compacted");

			// Clean up after ourselves, release the second list and force collection
			shouldfit = null;
			second.Clear();
			second = null;
			GC.Collect(2, GCCollectionMode.Forced);

			host.MemoryUsed("All memory cleaned up");
		}
	}
}
